
#region Imports

using System;
using System.Collections.Generic;

#endregion

namespace Justa.Service
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <typeparam name="T"></typeparam>
    /// 
    /// <param name="state"></param>
    /// 
    public delegate void Executor<T>(T state);

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <typeparam name="T"></typeparam>
    /// 
    /// <param name="state"></param>
    /// 
    public delegate void Reversable<T>(T state);

    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <typeparam name="T"></typeparam>
    /// 
    public class ReversableAction<T>
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        private Stack<Action> stack = new Stack<Action>();

        /// <summary>
        /// 
        /// </summary>
        /// 
        private Executor<T> executor;

        /// <summary>
        /// 
        /// </summary>
        /// 
        private Reversable<T> reversable;

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="executor"></param>
        /// <param name="reversable"></param>
        /// 
        public ReversableAction(Executor<T> executor, Reversable<T> reversable)
        {
            if (executor == null)
            {
                throw new ArgumentException("Executor is null", "executor");
            }

            if (reversable == null)
            {
                throw new ArgumentException("Reversable is null", "reversable");
            }

            this.executor = executor;
            this.reversable = reversable;
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="state"></param>
        /// 
        public void Execute(T state)
        {
            executor(state);
            stack.Push(new Action(state));
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        public void Reverse()
        {
            lock (stack)
            {
                if (stack.Count > 0)
                {
                    reversable(stack.Pop().State);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        public void ReverseAll()
        {
            lock (stack)
            {
                int count = stack.Count;
                for (int i = 0; i < count; i++) 
                {
                    reversable(stack.Pop().State);
                }
            }
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// 
        public void Clear()
        {
            stack.Clear();
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        public int Count
        {
            get
            {
                return stack.Count;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        internal class Action
        {
            /// <summary>
            /// 
            /// </summary>
            /// 
            private T state;

            /// <summary>
            /// 
            /// </summary>
            /// 
            public T State
            {
                get { return state; }
                set { state = value; }
            }

            /// <summary>
            /// 
            /// </summary>
            /// 
            /// <param name="state"></param>
            /// 
            public Action(T state)
            {
                this.state = state;
             }
        }
    }
}